/*
 * Copyright (C) 2019 Peng fei Pan <panpfpanpf@outlook.me>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package me.panpf.sketch.drawable;

import me.panpf.sketch.PixelMapDrawable;
import me.panpf.sketch.Sketch;
import me.panpf.sketch.decode.ResizeCalculator;
import me.panpf.sketch.request.ImageFrom;
import me.panpf.sketch.request.ShapeSize;
import me.panpf.sketch.shaper.ImageShaper;
import me.panpf.sketch.util.ExifInterface;
import me.panpf.sketch.util.Log;
import me.panpf.sketch.util.Utils;
import ohos.agp.components.Image;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.*;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.AlphaType;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * 可以改变 {@link PixelMapElement} 的形状和尺寸
 */
public class SketchShapeBitmapDrawable extends PixelMapDrawable implements SketchRefDrawable {
//    private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG;

    @NotNull
    private PixelMapElement bitmapDrawable;
    @Nullable
    private ShapeSize shapeSize;
    @Nullable
    private ImageShaper shaper;

    @NotNull
    private Paint paint;
    @NotNull
    private Rect srcRect;
    @Nullable
    private PixelMapShader bitmapShader;

    @Nullable
    private SketchRefDrawable refDrawable;
    @Nullable
    private SketchDrawable sketchDrawable;

    @NotNull
    private ResizeCalculator resizeCalculator;

    public SketchShapeBitmapDrawable(@NotNull Context context, @NotNull PixelMapElement bitmapDrawable,
                                     @Nullable ShapeSize shapeSize, @Nullable ImageShaper shaper) {
        super(bitmapDrawable.getPixelMap());
        PixelMapElement origin = bitmapDrawable;

        PixelMap bitmap = bitmapDrawable.getPixelMap();
        if (bitmap == null || bitmap.isReleased()) {
            throw new IllegalArgumentException(bitmap == null ? "bitmap is null" : "bitmap recycled");
        }

        if (shapeSize == null && shaper == null) {
            throw new IllegalArgumentException("shapeSize is null and shapeImage is null");
        }

        PixelMap pixelMap = bitmapDrawable.getPixelMap();
        PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
        if (shapeSize != null) {
            opts.size = new Size(shapeSize.getWidth(), shapeSize.getHeight());
            opts.pixelFormat = PixelFormat.ARGB_8888; // 必须设置成ARGB_8888,不然会造成色彩模糊
            opts.alphaType = pixelMap.getImageInfo().alphaType;
        }
        pixelMap = PixelMap.create(pixelMap, opts);
        bitmapDrawable = new PixelMapElement(pixelMap);

        this.bitmapDrawable = bitmapDrawable;
        this.paint = new Paint(/*DEFAULT_PAINT_FLAGS*/);
        this.paint.setAntiAlias(true);
        this.paint.setFilterBitmap(true);
        this.paint.setDither(true);
        this.srcRect = new Rect();
        this.resizeCalculator = Sketch.with(context).getConfiguration().getResizeCalculator();

        setShapeSize(shapeSize);
        setShaper(shaper);

        if (origin instanceof SketchRefDrawable) {
            this.refDrawable = (SketchRefDrawable) origin;
        }

        if (origin instanceof SketchDrawable) {
            this.sketchDrawable = (SketchDrawable) origin;
        }
    }

    @SuppressWarnings("unused")
    public SketchShapeBitmapDrawable(Context context, PixelMapElement bitmapDrawable, ShapeSize shapeSize) {
        this(context, bitmapDrawable, shapeSize, null);
    }

    @SuppressWarnings("unused")
    public SketchShapeBitmapDrawable(Context context, PixelMapElement bitmapDrawable, ImageShaper shaper) {
        this(context, bitmapDrawable, null, shaper);
    }

    @Override
    public void drawToCanvas(Canvas canvas) {
        Rect bounds = getBounds();
        PixelMap bitmap = bitmapDrawable.getPixelMap();
        if (bounds.isEmpty() || bitmap == null || bitmap.isReleased()) {
            return;
        }

        if (shaper != null && bitmapShader != null) {
            shaper.draw(canvas, paint, bounds);
        } else {
            Rect temp = !srcRect.isEmpty() ? srcRect : null;
            RectFloat src = temp != null ? new RectFloat(temp.left, temp.top, temp.right, temp.bottom) : null;

            RectFloat dst = new RectFloat(bounds.left, bounds.top, bounds.right, bounds.bottom);

            canvas.drawPixelMapHolderRect(new PixelMapHolder(bitmap), src, dst, paint);
//            canvas.drawBitmap(bitmap, !srcRect.isEmpty() ? srcRect : null, bounds, paint);
        }
    }

//    @Override
//    public void draw(@SuppressWarnings("NullableProblems") Canvas canvas) {
//        Rect bounds = getBounds();
//        PixelMap bitmap = bitmapDrawable.getPixelMap();
//        if (bounds.isEmpty() || bitmap == null || bitmap.isReleased()) {
//            return;
//        }
//
//        if (shaper != null && bitmapShader != null) {
//            shaper.draw(canvas, paint, bounds);
//        } else {
//            canvas.drawBitmap(bitmap, !srcRect.isEmpty() ? srcRect : null, bounds, paint);
//        }
//    }

    @Override
    public int getWidth() {
        return shapeSize != null ? shapeSize.getWidth() : bitmapDrawable.getWidth();
    }

    @Override
    public int getHeight() {
        return shapeSize != null ? shapeSize.getHeight() : bitmapDrawable.getHeight();
    }

//    public void invalidateSelf() {
//        final OnChangeListener callback = getCallback();
//        if (callback != null) {
//            callback.onChange(this);
//        }
//    }

    @Override
    public int getAlpha() {
        return (int) paint.getAlpha();
    }

    @Override
    public void setAlpha(int alpha) {
        final int oldAlpha = (int) paint.getAlpha();
        if (alpha != oldAlpha) {
            paint.setAlpha(alpha);
            invalidateSelf();
        }
    }

    public ColorFilter getColorFilter() {
        return paint.getColorFilter();
    }

    public void setColorFilter(ColorFilter cf) {
        paint.setColorFilter(cf);
        invalidateSelf();
    }

    public void setDither(boolean dither) {
        paint.setDither(dither);
        invalidateSelf();
    }

    public void setFilterBitmap(boolean filter) {
        paint.setFilterBitmap(filter);
        invalidateSelf();
    }

    public int getOpacity() {
        PixelMap bitmap = bitmapDrawable.getPixelMap();
        boolean hasAlpha = bitmap.getImageInfo().alphaType != AlphaType.UNKNOWN;
        return (hasAlpha || paint.getAlpha() < 255) ? AlphaType.PREMUL.getValue() : AlphaType.OPAQUE.getValue();
    }

    @Override
    public void setBounds(Rect bounds) {
        super.setBounds(bounds);
        onBoundsChange(bounds);
    }

    protected void onBoundsChange(Rect bounds) {

        int boundsWidth = bounds.right - bounds.left;
        int boundsHeight = bounds.bottom - bounds.top;
        int bitmapWidth = bitmapDrawable.getPixelMap().getImageInfo().size.width;
        int bitmapHeight = bitmapDrawable.getPixelMap().getImageInfo().size.height;

        if (boundsWidth == 0 || boundsHeight == 0 || bitmapWidth == 0 || bitmapHeight == 0) {
            srcRect.clear();
        } else if (Math.abs((double) bitmapWidth / (double) bitmapHeight - (double) boundsWidth / (double) boundsHeight) <= 0) {
            srcRect.left = 0;
            srcRect.top = 0;
            srcRect.right = bitmapWidth;
            srcRect.bottom = bitmapHeight;
        } else {
            Image.ScaleMode scaleType = shapeSize != null ? shapeSize.getScaleType() : Image.ScaleMode.STRETCH;
            ResizeCalculator.Mapping mapping = resizeCalculator.calculator(bitmapWidth, bitmapHeight, boundsWidth, boundsHeight, Utils.coverScaleMode(scaleType), true);

            srcRect.left = mapping.srcRect.left;
            srcRect.top = mapping.srcRect.top;
            srcRect.right = mapping.srcRect.right;
            srcRect.bottom = mapping.srcRect.bottom;
        }

        if (shaper != null && bitmapShader != null) {
            float widthScale = (float) boundsWidth / bitmapWidth;
            float heightScale = (float) boundsHeight / bitmapHeight;

            // 缩放图片充满bounds
            Matrix shaderMatrix = new Matrix();
            float scale = Math.max(widthScale, heightScale);
            shaderMatrix.postScale(scale, scale);

            // 显示图片中间部分
            if (!srcRect.isEmpty()) {
                shaderMatrix.postTranslate(-srcRect.left * scale, -srcRect.top * scale);
            }

            shaper.onUpdateShaderMatrix(shaderMatrix, bounds, bitmapWidth, bitmapHeight, shapeSize, srcRect);
            bitmapShader.setShaderMatrix(shaderMatrix);
            paint.setShader(bitmapShader, Paint.ShaderType.PIXELMAP_SHADER);
        }
    }

    @NotNull
    public PixelMapElement getBitmapDrawable() {
        return bitmapDrawable;
    }

    @Nullable
    public ShapeSize getShapeSize() {
        return shapeSize;
    }

    public void setShapeSize(ShapeSize shapeSize) {
        this.shapeSize = shapeSize;
        invalidateSelf();
    }

    @Nullable
    public ImageShaper getShaper() {
        return shaper;
    }

    public void setShaper(@Nullable ImageShaper shaper) {
        this.shaper = shaper;

        if (this.shaper != null) {
            if (bitmapShader == null) {
                bitmapShader = new PixelMapShader(new PixelMapHolder(bitmapDrawable.getPixelMap()),
                        Shader.TileMode.REPEAT_TILEMODE, Shader.TileMode.REPEAT_TILEMODE);
                paint.setShader(bitmapShader, Paint.ShaderType.PIXELMAP_SHADER);
            }
        } else {
            if (bitmapShader != null) {
                bitmapShader = null;
                paint.setShader(null, null);
            }
        }

        invalidateSelf();
    }

    @Nullable
    @Override
    public String getKey() {
        return sketchDrawable != null ? sketchDrawable.getKey() : null;
    }

    @Nullable
    @Override
    public String getUri() {
        return sketchDrawable != null ? sketchDrawable.getUri() : null;
    }

    @Override
    public int getOriginWidth() {
        return sketchDrawable != null ? sketchDrawable.getOriginWidth() : 0;
    }

    @Override
    public int getOriginHeight() {
        return sketchDrawable != null ? sketchDrawable.getOriginHeight() : 0;
    }

    @Nullable
    @Override
    public String getMimeType() {
        return sketchDrawable != null ? sketchDrawable.getMimeType() : null;
    }

    @Override
    public int getExifOrientation() {
        return sketchDrawable != null ? sketchDrawable.getExifOrientation() : ExifInterface.ORIENTATION_UNDEFINED;
    }

    @Override
    public int getByteCount() {
        return sketchDrawable != null ? sketchDrawable.getByteCount() : 0;
    }

    @Nullable
    @Override
    public PixelFormat getBitmapConfig() {
        return sketchDrawable != null ? sketchDrawable.getBitmapConfig() : null;
    }

    @Nullable
    @Override
    public ImageFrom getImageFrom() {
        return sketchDrawable != null ? sketchDrawable.getImageFrom() : null;
    }

    @Nullable
    @Override
    public String getInfo() {
        return sketchDrawable != null ? sketchDrawable.getInfo() : null;
    }

    @Override
    public void setIsDisplayed(@NotNull String callingStation, boolean displayed) {
        if (refDrawable != null) {
            refDrawable.setIsDisplayed(callingStation, displayed);
        }
    }

    @Override
    public void setIsWaitingUse(@NotNull String callingStation, boolean waitingUse) {
        if (refDrawable != null) {
            refDrawable.setIsWaitingUse(callingStation, waitingUse);
        }
    }

    @Override
    public boolean isRecycled() {
        return refDrawable == null || refDrawable.isRecycled();
    }
}